package cs236369.webserver.xml;

import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import cs236369.webserver.DTOs.HandlerMapping;
import cs236369.webserver.DTOs.MimeMapping;
import cs236369.webserver.DTOs.ServerConfigDTO;
//TODO
/* Only this is left to handle
 * <type-handlers>
		<type-handler class="cs236369.webserver.TSPEngine">
			<parameter name="jre_path" value="C:\Program Files\Java\jdk1.6.0_24\" />
			<extension>tsp</extension>
			<extension>dsp</extension>
		</type-handler>
	</type-handlers>

 */
public class XMLParser {
	public Collection<MimeMapping> getMimesFromConfigXML(String configFilename)
			throws ParserConfigurationException, SAXException, IOException {
		Element docEle = parseXmlFile(configFilename);
		NodeList nl = docEle.getElementsByTagName("mime-mapping");
		return getMime(nl);
	}
	
	public Collection<HandlerMapping> getHandlerAndExtentions(String configFilename)
			throws ParserConfigurationException, SAXException, IOException {
		Element docEle = parseXmlFile(configFilename);
		NodeList nl = docEle.getElementsByTagName("type-handler");
		return getHandlers(nl);
	}

	private Collection<HandlerMapping> getHandlers(NodeList nl) {
		Collection<HandlerMapping> res = new LinkedList<HandlerMapping>();
		if (nl != null && nl.getLength() > 0) {
			for(int i=0 ; i<nl.getLength() ; i++) {
				Element ele = (Element) nl.item(i);
				HandlerMapping hm = getHandlerMapping(ele);
				res.add(hm);
			}
		}
		return res;
	}

	private HandlerMapping getHandlerMapping(Element ele) {
		String className = ele.getAttribute("class");
		Map<String,String> params = getParams(ele.getElementsByTagName("parameter"));
		Collection<String> extentions = getExtentions(ele.getElementsByTagName("extension"));
		return new HandlerMapping(className, params, extentions);
	}

	private Collection<String> getExtentions(NodeList extentions) {
		Collection<String> res = new LinkedList<String>();
		if(extentions != null && extentions.getLength()>0) {
			for(int i=0 ; i<extentions.getLength() ; i++) {
				Element ele = (Element) extentions.item(i);
				String ext = ele.getFirstChild().getNodeValue();
				res.add(ext);
			}
		}
		return res;
	}

	private Map<String,String> getParams(NodeList parameters) {
		Map<String,String> res = new TreeMap<String,String>();
		if(parameters != null && parameters.getLength()>0) {
			for(int i=0 ; i<parameters.getLength() ; i++) {
				Element par = (Element) parameters.item(0);
				String name = par.getAttribute("name");
				String value = par.getAttribute("value");
				res.put(name, value);
			}
		}
		return res;
	}

	public ServerConfigDTO getServerConfigDTO(String configFilename)
			throws ParserConfigurationException, SAXException, IOException {
		Element docEle = parseXmlFile(configFilename);
		int numOfSocketReaderThreads = 0;
		int numOfReqHandlerThreads = 0;
		String serverBaseDir = null;
		int serverListeningPort = 0;
		int sizeOfSocketQueue = 0;
		int sizeOfReqQueue = 0;
		String welcomeFileName = null;
		Element el;
		NodeList nl = docEle.getElementsByTagName("socket-readers");
		if (nl != null && nl.getLength() > 0) {
			el = (Element) nl.item(0);
			numOfSocketReaderThreads = getIntValue(el, "multi");
		}
		nl = docEle.getElementsByTagName("request-handlers");
		if (nl != null && nl.getLength() > 0) {
			el = (Element) nl.item(0);
			numOfReqHandlerThreads = getIntValue(el, "multi");
		}
		welcomeFileName = getTextValue(docEle, "welcome-file");

		serverListeningPort = Integer.parseInt(docEle.getAttribute("port"));
		serverBaseDir = docEle.getAttribute("base");

		nl = docEle.getElementsByTagName("socket-queue");
		if (nl != null && nl.getLength() > 0) {
			el = (Element) nl.item(0);
			sizeOfSocketQueue = getIntValue(el, "size");
		}
		nl = docEle.getElementsByTagName("request-queue");
		if (nl != null && nl.getLength() > 0) {
			el = (Element) nl.item(0);
			sizeOfReqQueue = getIntValue(el, "size");
		}
		return new ServerConfigDTO(serverBaseDir, serverListeningPort,
				numOfSocketReaderThreads, numOfReqHandlerThreads,
				sizeOfSocketQueue, sizeOfReqQueue, welcomeFileName);
	}

	private Element parseXmlFile(String configFilename)
			throws ParserConfigurationException, SAXException, IOException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document dom = db.parse(configFilename);
		// get the root element
		Element docEle = dom.getDocumentElement();
		return docEle;
	}
	

	private MimeMapping getMimeMapping(Element el) {
		String ext = getTextValue(el, "extension");
		String type = getTextValue(el, "mime-type");
		MimeMapping e = new MimeMapping(ext, type);
		return e;
	}

	private Collection<MimeMapping> getMime(NodeList nl) {
		Collection<MimeMapping> mimes = new LinkedList<MimeMapping>();
		if (nl != null && nl.getLength() > 0) {
			for (int i = 0; i < nl.getLength(); i++) {
				Element el = (Element) nl.item(i);
				MimeMapping e = getMimeMapping(el);
				mimes.add(e);
			}
		}
		return mimes;
	}

	private String getTextValue(Element ele, String tagName) {
		String textVal = null;
		NodeList nl = ele.getElementsByTagName(tagName);
		if (nl != null && nl.getLength() > 0) {
			Element el = (Element) nl.item(0);
			textVal = el.getFirstChild().getNodeValue();
		}
		return textVal;
	}

	private int getIntValue(Element ele, String tagName) {
		return Integer.parseInt(getTextValue(ele, tagName));
	}

}
